class ReportsController < ApplicationController

  before_filter :login_required
  before_filter :set_reports_title
  before_filter :load_report_response, :only => [:view, :edit, :update, :auto_save]
  before_filter :load_report_request, :only => [:admin_view_pending, :admin_view_completed_report, 
                                                :admin_view_completed_completeness, 
                                                :admin_edit, :admin_update]                                                 
  before_filter :admin_only, :only => [:new, :create, :admin_edit, :admin_update]
    rescue_from Exception, :with => :render_error

  def render_error(exception)
    logger.info "User logged : #{current_user.inspect}"
    log_error(exception)
    render :template => "/errors/error.html.erb"
  end
 
  def index
     if current_user.is_only_xb?
        redirect_to(:action => 'xb_index')
      else
        redirect_to(:action => 'pending')
      end
  end
  
  def xb_index
    # TODO: Join all xb roles report responses
    if not(current_user.is_only_xb?)
       redirect_to(:action => 'pending')
    end
    @report_responses_pending=Array.new
    current_user.roles.each{|role|
      @report_responses_pending=@report_responses_pending+ReportResponse.get_pending(role)
    }
    
  end
    
  
  def pending
    if current_user.is_only_xb?
      redirect_to(:action => 'xb_index')
      return
    end
    # TODO: Redirect to xb_index or standard grid depending on role
    if current_user.is_admin
      @all_reports_groups = ReportRequest.pending
    end

    @reports_groups = current_user.roles.map do |role|
      [ ReportResponse.get_pending(role), role ]
    end
    
    @reports_xb_status_groups = current_user.roles.select(&:is_country_coordinator).map do |role|
      [ ReportResponse.get_country_xb_status(role, :not_submitted => true), role ]
    end
  end    
  
  def completed  
    @reports_groups = current_user.roles.map do |role|
      [ ReportResponse.get_completed(role), role ]
    end
      
    # HACK: admin is a special case that has access to all reports    
    if current_user.is_admin?
      @all_reports_groups = ReportRequest.completed
    end
  end
  
  def view
    role=@report_response.owner_role
    if role.is_country_coordinator 
      view_cc
    end
    if role.is_strategy_coordinator
      @maps=ReportsController.strategy_coordinator_view(role,@report_response)
      render :action => "sc_view"
    end
  end

  def view_cc
    report = @report_response.report
    @xb_summary = {}
    
    report.groupings.each do |group|
      @xb_summary[group] = {}
      group.questions_for_role(@report_response.owner_role).each do |question|
        if question.is_yes_no
          question_by_role = question.for_role @report_response.owner_role
          if question_by_role.first_column_auto?
            i = 0
            xbs_list=@report_response.owner_role.coordinated_xbs.each do |xb|
              answer = @report_response.xb_answer(question, xb)
              i += 1 if !answer.nil? and answer.is_yes
            end
            @xb_summary[group][question] = i
          end
        end
      end
    end
    
    render :action => "cc_view"
  end
  
  def admin_view_pending
    load_completeness
    render :action => "admin_view_pending"
  end
  
  def admin_view_completed_report
    @responses = @report_request.get_responses_grouped_by_xb
    @groupings = Grouping.all.select {|g| not g.strategy.nil? }
    @groupings = @groupings.sort { |g1, g2| g1.strategy.order <=> g2.strategy.order } 
    
    render :action => 'admin_view_completed'
  end
  
  def admin_view_completed_completeness
    load_completeness
    
    render :action => 'admin_view_completed_completeness'
  end
  
  def admin_edit
    logger.info "Entering to admin_edit view. User id: #{current_user.id}, ReportRequest id: #{@report_request.id}"
  end
  
  def admin_update
    if @report_request.update_attributes(params[:report_request])
      flash[:notice] = _('Report Request was successfully updated.')
      logger.info "Report Request was successfully updated. User id: #{current_user.id}, ReportRequest id: #{@report_request.id}"
      redirect_to :action => 'index'
    else
      flash[:error_notice] = _('An error occurred updating the Report Request')
      logger.info "An error occurred updating the Report Request. User id: #{current_user.id}, ReportRequest id: #{@report_request.id}, Error:#{@report_request.errors}"
      render :action => 'admin_edit'
    end
  end
  
  def edit
    logger.info "Entering to edit view. Role id: #{@report_response.owner_role.id}, ReportResponse id: #{@report_response.id}"
    load_sc_responses
    load_confirm_submit
    @tab_selected=0
  end
  
  def update
    logger.info "Updating report response. Role id: #{@report_response.owner_role.id}, ReportResponse id: #{@report_response.id}"
    is_submitting = params[:report_response][:is_submitted] == '1'
    @tab_selected = params[:tab_selected]
    was_submitted = @report_response.is_submitted

    if !update_report
      load_sc_responses
      load_confirm_submit
      flash[:error_notice] = _("Some errors ocurred while trying to save the information, please check the information in all the strategies tabs")
      logger.info "Updated was not succesfully. Role id: #{@report_response.owner_role.id}, ReportResponse id: #{@report_response.id}"
      @report_response.is_submitted=false
      render(:action=>'edit')
      return
    end
      
    description = flash[:notice] = case
      when is_submitting then _('Report has been submitted')
      when was_submitted then _('Completed report has been updated')
      else _("Report updated to") + " #{@report_response.completed_description}"
    end
   
    begin
      ReportEdit.create! :description => description , :user => current_user , :report_response => @report_response
    rescue
      flash[:error_notice] = _("Some errors ocurred while trying to save the information, please fill the fields of the responses")
      logger.info "Updated was not succesfully. Role id: #{@report_response.owner_role.id}, ReportResponse id: #{@report_response.id}"
    end
    logger.info "Updated succesfully. Role id: #{@report_response.owner_role.id}, ReportResponse id: #{@report_response.id}"
    
    if is_submitting
      redirect_to :action => was_submitted ? "completed" : "pending"
    else
      load_sc_responses
      load_confirm_submit
      render :action => 'edit'
    end
  end
  
  def auto_save
    logger.info "Auto Saving responses. Role id: #{@report_response.owner_role.id}, ReportResponse id: #{@report_response.id}"
    update_report
    render :text => @report_response.completed_description
  end
  
  def update_report
    # IMPORTANT: first update question options THEN update report_response
    # cause report_response update triggers the calculation of the completed percentage
    update_question_options_succesfull = update_question_options
    
    update_attributes_succesfull=@report_response.update_attributes params[:report_response]
    
    return update_attributes_succesfull && update_question_options_succesfull
  end
  

  private :update_report
  
  
  def new
    @report_request = ReportRequest.new
  end

  def create
    begin
      logger.info "Creating a report. User requestor id: #{current_user.id}"
      @report_request = ReportRequest.new(params[:report_request])
      
      if (params[:period]!=  "-1" && params[:year] != "-1" )
        year = params[:year].to_i
        
        if (params[:period]=="first_period")
          month_start = 1
          month_end = 6
        elsif(params[:period]=="second_period")
          month_start = 7
          month_end = 12
        end
        
        @report_request.period_start = Date.new(year, month_start, 1)
        @report_request.period_end = Date.new(year, month_end, 1)
      end
      
      @report_request.requestor = current_user
      @report_request.report = Report.first
      
      @report_request.save!
      
        
      #TODO if some of this report response fails need to rollback everything  
      # Create Report Responses for each role
      Role.all.each do | r |
        ReportResponse.create!(:is_submitted => false, :report_request => @report_request, :owner_role => r)
      end
        
      flash[:notice] = _('Report was successfully created.')
      logger.info "Report was successfully created. User requestor id: #{@report_request.requestor.id}"
      redirect_to(:action => "new")  
    rescue
      flash[:error_notice] = _('An error occurred creating the Report Request')
      logger.info "An error occurred creating the Report Request. User requestor id: #{@report_request.requestor.id}, Error:#{@report_request.errors}"
      render :action => "new" 
    end
  end 
  
  #returns 2 maps coordinate_types_question_map , coordinate_types_role_map
    
  def self.strategy_coordinator_view(role,report_responses)
    coordinate_types_question_map=Hash[Strategy.name => Hash.new, Country.name => Hash.new, Site.name => Hash.new]
    coordinate_types_role_map=Hash[Strategy.name => Set.new, Country.name => Set.new, Site.name => Set.new]
    questions=role.strategy.grouping.questions
    
    report_responses.report_request.report_responses.each {|repo_response|
      role=repo_response.owner_role
      #dont include sc from a different strategy
      if !(role.is_strategy_coordinator && role!=report_responses.owner_role)
        #add the role to the roles map
        coordinate_types_role_map[role.coordinated_type].add(role)  
        #add the answer to the questions map        
        role_map=coordinate_types_question_map[role.coordinated_type]
        questions.each {|q|
          #check if this question applies for the current role
          if(!q.text_for(role).nil?)
            if !role_map.has_key?(q)
              role_map[q]=Hash.new
            end
            question_map=role_map[q]
            question_map[role]=repo_response.answer(q)
          end
        }
      end
    }
    response=[coordinate_types_question_map,coordinate_types_role_map]
    return response
  end

  private
  
  def load_completeness
    @strategy_responses = @report_request.strategy_responses.sort{|a,b| a.owner_role.strategy.order <=> b.owner_role.strategy.order}
    @strategy_responses_by_country = @report_request.strategy_responses_by_country
    @cc_responses_by_country = @report_request.cc_responses_by_country
    @xb_responses_by_country = @report_request.xb_responses_by_country
    
    # Overall for strategies
    overall_strategy = average @strategy_responses
    
    # Overall for xb of all countries
    xb_responses = @xb_responses_by_country.values.flatten
    overall_xb_site = average xb_responses
    
    # Overall for cc of all countries
    cc_responses = @cc_responses_by_country.values.flatten
    overall_cc = average cc_responses
    
    # Total overall (strategy, cc, xb)
    overall_total = (overall_strategy + overall_xb_site + overall_cc) / 3
    
    # Send values as a hash
    @overall = {:total => overall_total, :xb_site => overall_xb_site, :cc => overall_cc, :strategy => overall_strategy}
    
    # Calculate overall by country
    @overall_by_country = Hash.new
    @countries = Country.find(:all, :order=>"name")
    @countries.each do |country|
      responses = []
      responses += @strategy_responses_by_country[country] unless @strategy_responses_by_country[country].nil?
      responses += @cc_responses_by_country[country] unless @cc_responses_by_country[country].nil?
      responses += @xb_responses_by_country[country] unless @xb_responses_by_country[country].nil?
      @overall_by_country[country] = average responses
    end
    
    # Calculate overall by strategy for CC and XB
    @strategy_xb_responses = {}
    @strategy_cc_responses = {}
    Strategy.all.each do |strategy|
      xb_strat = xb_responses.map{|rr| rr.get_completed_for_strategy(strategy)}
      cc_strat = cc_responses.map{|rr| rr.get_completed_for_strategy(strategy)}
      @strategy_xb_responses[strategy] = average_list xb_strat
      @strategy_cc_responses[strategy] = average_list cc_strat
    end
    
    @overall[:strategy_xb] = average_list @strategy_xb_responses.values.flatten
    @overall[:strategy_cc] = average_list @strategy_cc_responses.values.flatten
  end
  
  def average(responses)
    sum = responses.inject(0){|sum,response| sum + response.completed}
    length = responses.length
    if length == 0 then 0 else Float(sum) / Float(length) end
  end
  
  def average_list(list)
    sum = list.inject(0){|sum, value| sum + value}
    length = list.length
    if length == 0 then 0 else Float(sum) / Float(length) end
  end

  def load_report_response
    @report_response = ReportResponse.find_by_id params[:id]
    render :nothing => true, :status => 404 and return false if @report_response.nil? 
    render :nothing => true, :status => 403 and return false if !@report_response.owner_users.include?(current_user) and not current_user.is_admin?
    true
  end
  
  def load_report_request
    @report_request = ReportRequest.find_by_id params[:id]
    render :nothing => true, :status => 404 and return false if @report_request.nil? 
    true
  end
  
  def set_reports_title
    @title = _("Implementation Report Management")
    @active_tab = :reports
  end
  
  # Updates relationships between Answer and QuestionOption for 
  # question of kind 'option'
  def update_question_options
    updated_succesfull=true
    answers_options = params[:answers_options]
    
    if answers_options.nil?
      return updated_succesfull
    end
    
    answers_options.each do |answer_id, options|
      answer = Answer.find(answer_id)
      answer.question_options.clear
      options.each do |option_id, values|
        next if values[:state].nil?
        
        option = QuestionOption.find_by_id option_id
        state = values[:state] == '1'
        AnswersQuestionOption.create! :answer => answer, :question_option => option, :state => state
        
        value_text = values[:value_text]
        
        if value_text
          text = state ? value_text : nil
          answer.value_text = text
          if ! answer.save!
            flash[:notice] = answer.errors
            updated_succesfull=false
          end  
        end
      end
    end
    return updated_succesfull
  end
  
  def load_confirm_submit
   # If the owner role is a CC, check if any of his coordinated XBs has an 
    # unsubmitted report, in order to warn him before submitting his response.
    @confirm_submit = false
    if @report_response.owner_role.is_country_coordinator
      xbs = @report_response.owner_role.coordinated_xbs
      report_request = @report_response.report_request
      unsubmitted_responses_of_xbs = report_request.report_responses.select do |response|
        xbs.include? response.owner_role and not response.is_submitted
      end
      @confirm_submit = !unsubmitted_responses_of_xbs.empty?
    end
  end
  
  def load_sc_responses
    if @report_response.owner_role.is_strategy_coordinator
      @repo_responses = @report_response.find_by_type("Strategy")
    end
  end
  
end
